package com.anlogic.sdk.programfpga.utils;

import java.io.File;

import org.apache.logging.log4j.Logger;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ui.console.MessageConsole;

import com.anlogic.sdk.openocd.OpenocdInitConfig;
import com.anlogic.sdk.openocd.OpenocdInitParam;
import com.anlogic.sdk.openocd.model.JtagCable;
import com.anlogic.sdk.openocd.model.RemoteTarget;
import com.anlogic.sdk.programfpga.ProgramFPGAModel;
import com.anlogic.sdk.tools.exetool.ExeTool;
import com.anlogic.sdk.utils.EnvUtils;
import com.anlogic.sdk.utils.FileUtils;
import com.anlogic.sdk.utils.ICommandLauncher;
import com.anlogic.sdk.utils.LogUtils;
import com.anlogic.sdk.utils.ProcLauncherUtils;
import com.anlogic.sdk.utils.StringUtils;
import com.anlogic.sdk.utils.UtilsPlugin;
import com.anlogic.sdk.utils.options.ChipType;

public class ProgramFPGAUtils {
	private static Logger logger = LogUtils.getLogger(ProgramFPGAUtils.class);
	
	private static final String LAUNCH_DOWNLOAD_SERVER = "launch_download_server";
	
	private static final String PROGRAM_FPGA = "program_fpga";

	private static final String PROGRAM_FPGA_OPENOCD = "program_fpga_openocd";

	private static final String DOWNLOAD_SERVER_SCRIPT_FILE = EnvUtils.getIDERootPath() + "/tools/launch_download_server";
	
	private static final String PROGRAM_FPGA_SCRIPT_FILE = EnvUtils.getIDERootPath() + "/tools/data/program_fpga.tcl";
	
	private static final String CFG_FILE_PRERFIX = EnvUtils.getIDERootPath() + "/toolchain/openocd/fpsoc/share/openocd/scripts/target";
	
	private static final String CONNECT_CABLE_CMD = "connect_cable \"-host_name\" \"%s:%s\"\n";
	
	private static final String DOWNLOAD_CMD = "set ret [catch {download -bit %s -mode %s -spd %d -cable %d}]\nif {$ret == 0} {puts \"Download Success!\"} else {puts \"Download Failed!\"}\nexit\n";
	
	private static final String OPENOCD_DOWNLOAD_CMD = "init\n"
											 + "puts \"Info : Program fpga starting...\"\n"
											 + "set ret [pld load 0 %s]\n"
											 + "puts \"Info : $ret\"\n"
											 + "puts \"Info : Program fpga success\"\n";
	
	public static void writeScript(ProgramFPGAModel model) {
		logger.info("Write program fpga script...");
		writeServerScript(model);
		writeClientScript(model);
		logger.info("Write program fpga script done");
	}
	
	public static void writeServerScript(ProgramFPGAModel model) {
		String preCmd = "";
		RemoteTarget target = model.getRemoteTarget();
		if (target != null) {
			JtagCable cable = target.getCable();
			if (cable != null) {
				int initParam = 0;
				int freq = StringUtils.parseInt(cable.getFrequency());
				initParam = OpenocdInitParam.setJtagSpeed(initParam, freq);
				initParam = OpenocdInitParam.setHwserver(initParam, 1);
				String initCmd = OpenocdInitConfig.getInitParamCmd(initParam);
				preCmd = OpenocdInitConfig.getCommandLineCmd(initCmd);
				preCmd = preCmd + String.format(" -c \"adapter usb location %s\"", cable.getUsbPort());
			}
		}
		if (model.useOldMode()) {
			preCmd = preCmd + " " + OpenocdInitConfig.getRbbServerCmd(model.getPort());
		}
		String cfgFile = getCfgFile(model.useOldMode(), model.getChip());
		String serverCmd = OpenocdInitConfig.getCmdWithInit(preCmd, cfgFile, "");
		writeScript(getScriptFileName(DOWNLOAD_SERVER_SCRIPT_FILE), serverCmd);
	}
	
	public static void writeClientScript(ProgramFPGAModel model) {
		if (model.useOldMode()) {
			String downloadStr = String.format(DOWNLOAD_CMD, model.getBitFile(), model.getMode(), model.getSpeedID(), model.getCable());
			String content = String.format(CONNECT_CABLE_CMD, model.getHost(), model.getPort()) + downloadStr;
			FileUtils.write(new File(PROGRAM_FPGA_SCRIPT_FILE), content);
		} else {
			String content = String.format(OPENOCD_DOWNLOAD_CMD, model.getBitFile());
			FileUtils.write(new File(PROGRAM_FPGA_SCRIPT_FILE), content);
		}
	}
	
	public static String getScriptFileName(String baseName) {
		String scriptFileName = baseName;
		if (!Platform.getOS().equals("linux")) {
			scriptFileName += ".bat";
		}
		return scriptFileName;
	}
	
	public static void writeScript(String filePath, String cmd) {
		if (Platform.getOS().equals("linux")) {
			cmd = "#!/bin/bash\n\n" + cmd;
		}
		FileUtils.write(new File(filePath), cmd);
	}
	
	public static String getCfgFile(boolean useOldMode, String chipName) {
		if (useOldMode) {
			String cfgFile = CFG_FILE_PRERFIX + "/anlogic/onecable.cfg";
			if (ChipType.isSOC66080(chipName)) {
				cfgFile = CFG_FILE_PRERFIX + "/soc/rbb.cfg";
			}
			return cfgFile;
		} else {
//			String cfgFile = CFG_FILE_PRERFIX + "/anlogic/al_fpga.cfg";
//			if (ChipType.isSOC66080(chipName)) {
//				cfgFile = CFG_FILE_PRERFIX + "/soc/fpga.cfg";
//			}
			String cfgFile = OpenocdInitConfig.getCfgFile(chipName);
			return cfgFile;
		}
	}
	
	public static String getLaunchHwServerCmd(ProgramFPGAModel model) {
		RemoteTarget target = model.getRemoteTarget();
		if (target != null) {
			JtagCable cable = target.getCable();
			if (cable != null) {
				int freq = StringUtils.parseInt(cable.getFrequency());
				return String.format("launch_hw_server %s %s", model.getPort(), freq);
			}
		}
		return String.format("launch_hw_server %s", model.getPort());
	}
	
	public static String getDownloadCmd(boolean useOldMode) {
		if (useOldMode) {
			return PROGRAM_FPGA;
		} else {
			return PROGRAM_FPGA_OPENOCD;
		}
	}
	
	public static String programFPGA() {
		ExeTool anlogicTool = new ExeTool("Download Tool");
		String downloadCmd = getDownloadCmd(true);
		return anlogicTool.launch(downloadCmd);
	}
	
	public static void programFPGA(ProgramFPGAModel model) {
		File pwd = new File(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
		MessageConsole downloadConsole = UtilsPlugin.getDefault().getMessageConsole("Download Tool");
		ICommandLauncher downloadLauncher = UtilsPlugin.getDefault().getLauncher(downloadConsole);
		
		String downloadCmd = getDownloadCmd(model.useOldMode());
		
		Job job = new Job("Download Tool executing...") {
			@Override
			protected IStatus run(IProgressMonitor monitor) {
				logger.info("Program fpga starting...");
				downloadLauncher.asyncLaunch(downloadCmd, pwd, null);
				while (!downloadLauncher.hasTerminated()) {
					try {
						Thread.sleep(100L);
					} catch (InterruptedException e) {
						logger.error("Unexpected error occurred {}", e.getMessage());
					}
					if (monitor.isCanceled()) {
						downloadLauncher.terminate();
					}
					if (downloadLauncher.hasTerminated()) {
						logger.info("Program fpga done.");
					}
				}
				monitor.done();
				return Status.OK_STATUS;
			}
		};
		job.schedule();
	}

	public static void programFPGAWithServer(ProgramFPGAModel model) {
		File pwd = new File(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
		MessageConsole serverConsole = UtilsPlugin.getDefault().getMessageConsole("Download Server Tool");
		MessageConsole downloadConsole = UtilsPlugin.getDefault().getMessageConsole("Download Tool");
		ICommandLauncher serverLauncher = UtilsPlugin.getDefault().getLauncher(serverConsole);
		ICommandLauncher downloadLauncher = UtilsPlugin.getDefault().getLauncher(downloadConsole);
		
		serverLauncher.setRedirectErrToOut(true);
		serverLauncher.setFilterErrorMessage(true);
		downloadLauncher.setFilterErrorMessage(true);
		
		logger.info("kill openocd start...");
		ProcLauncherUtils.killExecute("openocd");
		logger.info("kill openocd done");
		
		if (ChipType.isDr1(model.getChip()) || ChipType.isSOC66080(model.getChip()) || ChipType.isDR1M350(model.getChip())) {
			logger.info("Release the lockdown status...");
			ProcLauncherUtils.releaseLockdown();
			logger.info("Release the lockdown status done");
		}
		
		boolean useOldMode = model.useOldMode();
		
		String hwServerCmd = LAUNCH_DOWNLOAD_SERVER;
//		String hwServerCmd = getLaunchHwServerCmd(model);
		String downloadCmd = getDownloadCmd(useOldMode);
		
		Job job = new Job("Download Tool executing...") {
			@Override
			protected IStatus run(IProgressMonitor monitor) {
				logger.info("Program fpga starting...");
				serverLauncher.asyncLaunch(hwServerCmd, pwd, null);
				downloadLauncher.asyncLaunch(downloadCmd, pwd, null);
				while (!serverLauncher.hasTerminated()) {
					String ret = serverLauncher.flush();
					if (!ret.isEmpty() && !useOldMode) {
						downloadLauncher.printStdOutMessage(ret);
					}
					try {
						Thread.sleep(100L);
					} catch (InterruptedException e) {
						logger.error("Unexpected error occurred : {}", e.getMessage());
					}
					if (monitor.isCanceled()) {
						downloadLauncher.terminate();
						serverLauncher.terminate();
					}
					if (downloadLauncher.hasTerminated()) {
						serverLauncher.terminate();
						logger.info("Program fpga done.");
					}
				}
				monitor.done();
				return Status.OK_STATUS;
			}
		};
		job.schedule();
	}
}
